package com.edu.tool.bc;

import com.edu.tool.mapper.bc.SysBCMapper;
import com.edu.tool.util.SpringUtil;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;


public class SysBusComp {

    public SysBcDef bcdef;

    public Set<String>         activeFields  = new HashSet<>();
    public Map<String, Object> fieldValMap   = new HashMap<>();

    public HashMultimap<String, QuerySpec> fieldQuerySpec =  HashMultimap.create();
    public Map<String, OrderBySpec> fieldSortSpec = new HashMap<>();

    private SysBCMapper sysBCMapper;

    private Integer id;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public SysBusComp(SysBcDef bcdef) {
        this.bcdef  = bcdef;
        sysBCMapper = SpringUtil.getBean(SysBCMapper.class);
    }



    public Set<String> getActiveFields() {
        return activeFields;
    }

    public boolean activeField(String fieldName) {
        activeFields.add(fieldName);
        return true;
    }

    public void setFieldValue(String fieldName, Object value) {
        //if( bcdef.mfields.get(fieldName).isReadOnly())
        //    throw new RuntimeException("Field " + fieldName +" is readonly.");
        SysField field = bcdef.mfields.get(fieldName);
        List<SysPickMap> pickMapList = field.getPickMap();
        if(pickMapList != null){
            Map<String,Object> valMap = (Map<String,Object>)value;
            for (SysPickMap sysPickMap : pickMapList) {
                Object fval = valMap.get(sysPickMap.getSourceField());
                fieldValMap.put(fieldName,fval);
            }
        }else{
            fieldValMap.put(fieldName, value);
        }

    }

    public void setSearchSpec(String fieldName,String searchSpec){

    }

    public void setSortSpec(String sortSpec){

    }

    private void addLinkJoin(SysBcDef  parentBcdef,
                             String mvl,
                             SysLinkDef linkDef,
                             StringBuilder joinClauseSB){
        Map<String,SysField>   fielddefs     = parentBcdef.mfields;
        Map<String,String>     bean2TableMap = parentBcdef.bean2Table;
        Map<String,SysField>     childFielddefs = null;


        String parentFieldName    = linkDef.getParentField();
        if (StringUtils.isEmpty(parentFieldName)){
            parentFieldName = "id";
        }

        SysField parentBeanField = fielddefs.get(parentFieldName);

        if (!parentBeanField.isMVF()) {
            String bfarray[]  = parentBeanField.getFullField().split("\\.");
            String parentTable = null;
            if (bfarray.length == 1) {
                parentTable = bean2TableMap.get(bcdef.baseBean);
            } else {
                parentTable =  bean2TableMap.get(bfarray[0]);
            }

            //Get child bc def
            SysBcDef childBc       = SysBcDef.getBc(linkDef.getChildBC());

            childFielddefs                            = childBc.mfields;
            Map<String,String>     childBean2TableMap = childBc.bean2Table;

            //Get child bc link child field
            String childFieldName    = linkDef.getChildField();
            if (StringUtils.isEmpty(parentFieldName)){
                childFieldName = "id";
            }
            SysField childBeanField = childFielddefs.get(childFieldName);

            String childbfarray[]  = childBeanField.getFullField().split("\\.");
            String childStartTable = null;
            if (childbfarray.length == 1) {
                childStartTable = childBean2TableMap.get(bcdef.baseBean);
            } else {
                childStartTable =  childBean2TableMap.get(bfarray[0]);
            }

            joinClauseSB.append(parentTable);
            joinClauseSB.append(" ");
            joinClauseSB.append(bfarray[0]); //alias
            joinClauseSB.append(" join ");

            String interTable = linkDef.getInterTable();
            String alias = mvl + "_"  + interTable;
            if(StringUtils.isEmpty(interTable)) { //join directly to child table
                joinClauseSB.append(childStartTable);
                joinClauseSB.append(" ");
                joinClauseSB.append(childbfarray[0]);

                joinClauseSB.append(" on ");
                joinClauseSB.append( parentBeanField.getFullField());

                joinClauseSB.append(" = ");
                joinClauseSB.append(  childBeanField.getFullField()  );
            }
            else//join to intertable and then to child table
            {
                String interParCol   = linkDef.getInterparentCol();
                String interChildCol = linkDef.getInterchildCol();
                alias = mvl + "_"  + interTable;
                joinClauseSB.append(interTable);
                joinClauseSB.append(" ");
                joinClauseSB.append(alias);

                joinClauseSB.append(" on ");
                joinClauseSB.append(  parentBeanField.getFullField()  );
                joinClauseSB.append(" = ");
                joinClauseSB.append( alias );
                joinClauseSB.append( ".");
                joinClauseSB.append(interParCol);

                //join to child table
                joinClauseSB.append(" join ");

                joinClauseSB.append(childStartTable);
                joinClauseSB.append(" ");
                joinClauseSB.append(childbfarray[0]);

                joinClauseSB.append(" on ");
                joinClauseSB.append( alias );
                joinClauseSB.append( ".");
                joinClauseSB.append(interChildCol);
                joinClauseSB.append(" = ");
                joinClauseSB.append(  childBeanField.getFullField()  );
            }
        }
        else
        {
            throw new RuntimeException("mvl parent should not be multi value field. ");
        }

    }

    private String buildQuerySql(SysBcDef bcdef){

        Set<String> joinTables = new HashSet<>();

        StringBuilder whereClause    = new StringBuilder();
        whereClause.append(" where ");


        //Step 1: condition fields processing
        Map<String,SysField>   fielddefs     = bcdef.mfields;
        Map<String,String>     bean2TableMap = bcdef.bean2Table;


        HashMultimap<String,String> mvl2FVal = HashMultimap.create();
        Boolean bFirst = true;

        for (Map.Entry<String,Object> entry : fieldValMap.entrySet()) {
            String fieldName = entry.getKey();

            SysField beanField = fielddefs.get(fieldName);
            if (!beanField.isMVF()) {  //join by field equals
                String bfarray[] = beanField.getFullField().split("\\.");
                if (bFirst) {
                    bFirst = false;
                } else {
                    whereClause.append(" and ");
                }

                if (bfarray.length == 1) {
                    joinTables.add(bcdef.baseBean);

                    whereClause.append(bcdef.baseBean);
                    whereClause.append(".");
                    whereClause.append(bfarray[0]);
                    whereClause.append(" = #{");
                    whereClause.append(fieldName);
                    whereClause.append("}");

                } else {
                    joinTables.add(bfarray[0]);

                    whereClause.append(beanField.getFullField());
                    whereClause.append(" = #{");
                    whereClause.append(fieldName);
                    whereClause.append("}");
                }
            }
            else //link
            {
                String mvlinkname  = beanField.getMvlink();
                mvl2FVal.put(mvlinkname,fieldName);
            }
        }

        HashMultimap<String,String> mvl2JoinTables = HashMultimap.create();

        //Step1.1 mvl condition fields processing
//        Set<String> mvls = mvl2FVal.keySet();
//        Map<String,String> f2ValKey = null;
//
//
//        for(String mvl:mvls)
//        {
//             SysLinkDef linkDef = bcdef.linkdefs.get(mvl);
//             SysBcDef childBc   = SysBcDef.getBc(linkDef.getChildBC());
//             Set<String> fields = mvl2FVal.get(mvl);
//
//            f2ValKey = new HashMap<>();
//             for(String  field:fields)
//             {
//                 Object val    = fieldValMap.get(field);
//                 SysField fdef = fielddefs.get(field);
//                 f2ValKey.put(fdef.getFullField(),field);
//             }
//
//             getMvlWhereClause(mvl,
//                     childBc,
//                     f2ValKey,
//                     whereClause,
//                     bFirst,
//                     mvl2JoinTables);
//        }

        //step2: select fields processing
        HashMultimap<String,String> mvl2SelF = HashMultimap.create();
        HashMap<String,String> alias2RF      = new HashMap<>();
        for (String fieldName:activeFields) {
            SysField beanField = fielddefs.get(fieldName);
            if (!beanField.isMVF()) { //join fields
                String bfarray[] = beanField.getFullField().split("\\.");

                if (bfarray.length == 1) {
                    joinTables.add(bcdef.baseBean);
                } else {
                    joinTables.add(bfarray[0]);
                }
            }else{ //link fields only take primary  record value (since  mvl fields are multiple values)
                String mvlinkname  = beanField.getMvlink();
                mvl2SelF.put(mvlinkname,fieldName);
            }
        }

        //Step 2.1: processing mvl select fields to get mvl join tables and alias
        Set<String>           selmvls        = mvl2SelF.keySet();
        Map<String,SysField>  childFielddefs = null;
        for(String mvl:selmvls) {
            SysMVLDef mvlDef       = bcdef.mvls.get(mvl);
            SysLinkDef linkDef     = bcdef.linkdefs.get(mvlDef.getLinkName());

            //Get child bc def
            SysBcDef childBc       =  SysBcDef.getBc(linkDef.getChildBC());

            childFielddefs                            = childBc.mfields;
            Map<String,String>     childBean2TableMap = childBc.bean2Table;

            //get all fields from current mvl
            Set<String>             fields            = mvl2SelF.get(mvl);

            for(String  field:fields)
            {
                SysField fdef       = fielddefs.get(field);
                SysField cbeanField = childFielddefs.get(fdef.getFullField());

                String bfarray[] = cbeanField.getFullField().split("\\.");
                if (bfarray.length == 1) {
                    mvl2JoinTables.put(mvl, childBc.baseBean);
                } else {
                    mvl2JoinTables.put(mvl,childBean2TableMap.get(bfarray[0]));
                }
                alias2RF.put(field,mvl + "_" + cbeanField.getFullField());
            }

            mvl2JoinTables.put(mvl, childBc.baseBean);
        }

        //using mvl join tables (collect from condition fields and select fields ) to form join table links
        HashMap mvl2JoinClause = Maps.newHashMap();
        Set<String> mvls = mvl2JoinTables.keySet();
        for( String mvl: mvls){


            SysMVLDef mvlDef       = bcdef.mvls.get(mvl);
            SysLinkDef linkDef     = bcdef.linkdefs.get(mvlDef.getLinkName());

            //Get child bc def
            SysBcDef childBc       =  SysBcDef.getBc(linkDef.getChildBC());

            Set<String> mvlJoinTables = mvl2JoinTables.get(mvl);

            String primaryIdFieldName  = mvlDef.getPrimaryIdField();
            if(StringUtils.isEmpty(primaryIdFieldName)) {
                throw new RuntimeException("should define a primary col for multi value link");
            }

            SysField primaryIdField  = fielddefs.get(primaryIdFieldName);
            String parentFArray[]    = primaryIdField.getFullField().split("\\.");

            joinTables.add(parentFArray[0]);

            //addLinkJoin(bcdef,mvl,linkDef,joinClauseSB);
            Set<String> mvlRealJoinTables = calculateJoinTables(childBc,mvlJoinTables);
            String JoinClause =  buildLinkJoinClauseUsingPrimaryField(primaryIdField,mvl,linkDef,mvlRealJoinTables);

            mvl2JoinClause.put(mvl,JoinClause);
        }

        Set<String> realJoinTables = calculateJoinTables(bcdef,joinTables);

        //join clause
        String baseBCJoinClause = generateJoinClause(bcdef,realJoinTables);
        StringBuilder JoinClasue = new StringBuilder();
        JoinClasue.append(baseBCJoinClause);
        mvl2JoinClause.forEach((mvl,mvlJoinClause) ->{
            JoinClasue.append(mvlJoinClause);
        });
        //select clause
        StringBuffer selectClause = new StringBuffer();
        selectClause.append("select ");
        selectClause.append(bcdef.baseBean);
        selectClause.append(".");
        selectClause.append("id");
        selectClause.append(" as ");
        selectClause.append(" id ");

        bFirst = false;
        for(String af: activeFields){
            SysField beanField = fielddefs.get(af);
            if (!beanField.isMVF()) {
                String bfarray[] = beanField.getFullField().split("\\.");

                if (bFirst) {
                    bFirst = false;
                } else {
                    selectClause.append(",");
                }

                if (bfarray.length == 1) {
                    selectClause.append(bcdef.baseBean);
                    selectClause.append(".");
                    selectClause.append(beanField.getFullField());
                    selectClause.append(" as ");
                    selectClause.append(af);
                } else {
                    selectClause.append(beanField.getFullField());
                    selectClause.append(" as ");
                    selectClause.append(af);
                }
            }else{
                if (bFirst) {
                    bFirst = false;
                } else {
                    selectClause.append(",");
                }
                String RF = alias2RF.get(af);
                selectClause.append(RF);
                selectClause.append(" as ");
                selectClause.append(af);
            }
        }

        selectClause.append(" ").append(JoinClasue).append(whereClause);
        return selectClause.toString();
    }

    private static Set<String> calculateJoinTables(SysBcDef bcdef, Set<String> joinTables) {
        Map<String, List<SysJoinDef>> toBean2JoinDef   = bcdef.toBean2JoinDef;
        Set<String> realJoinTables = new HashSet<String>();
        String baseBeanName   = bcdef.baseBean;
        joinTables.forEach(beanName -> {
            String parentBeanName = beanName;
            realJoinTables.add(beanName);
            while (!parentBeanName.equalsIgnoreCase(baseBeanName)){
                List<SysJoinDef> joinDefs = toBean2JoinDef.get(beanName);

                SysJoinDef joindef        = joinDefs.get(0);
                parentBeanName            = joindef.getParentBean();

                realJoinTables.add(parentBeanName);
            }
        });

        return realJoinTables;
    }

    private String generateJoinClause(SysBcDef bcdef,
                                       Set<String> joinTables ){
        Map<String,List<SysJoinDef>>  bean2JoinDef  =  bcdef.fromBean2JoinDef;
        Map<String, String> bean2TableMap = bcdef.bean2Table;

        //join tables from base table:
        Queue<String> queue = new LinkedList<>();
        queue.add(bcdef.baseBean);

        StringBuilder JoinClause = new StringBuilder();

        String baseTable         = bean2TableMap.get(bcdef.baseBean);
        JoinClause.append(" from ");
        JoinClause.append(baseTable);
        JoinClause.append(" ");
        JoinClause.append(bcdef.baseBean);

        while (!queue.isEmpty())
        {
            String currentBean   = queue.poll();

            List<SysJoinDef> joinDefList = bean2JoinDef.get(currentBean);

            List<String> nextBeans       = new LinkedList<>();
            if(joinDefList != null) {
                for (int i = 0; i < joinDefList.size(); i++) {
                    SysJoinDef joinDef = joinDefList.get(i);
                    String childBean   = joinDef.getChildBean();

                    if(joinTables.contains(childBean)){
                        String childTable  = bean2TableMap.get(childBean);
                        nextBeans.add(childBean);

                        String childCol  = joinDef.getChildField();
                        String parentCol = joinDef.getParentField();

                        JoinClause.append(" join ");
                        JoinClause.append(childTable);
                        JoinClause.append(" ");
                        JoinClause.append(childBean);

                        JoinClause.append(" on ");
                        JoinClause.append(currentBean);
                        JoinClause.append(".");
                        JoinClause.append(parentCol);
                        JoinClause.append("=");
                        JoinClause.append(childBean);
                        JoinClause.append(".");
                        JoinClause.append(childCol);
                    }
                }


                if (nextBeans != null) {
                    for (int i = 0; i < nextBeans.size(); i++) {
                        queue.add(nextBeans.get(i));
                    }
                }
            }
        }
        return JoinClause.toString();
    }

    private String buildLinkJoinClauseUsingPrimaryField(SysField primaryIdField,
                                                       String mvl,
                                                       SysLinkDef linkDef,
                                                       Set<String> mvlJoinTables) {

        String     childField                          = linkDef.getChildField();

        //Get child bc def
        SysBcDef childBc                               = SysBcDef.getBc(linkDef.getChildBC());
        Map<String, SysField> childFielddefs           = childBc.mfields;
        Map<String, List<String>>     beanDepson       = childBc.beanDepson;
        Map<String, List<SysJoinDef>> toBean2JoinDef   = childBc.toBean2JoinDef;
        Map<String, List<SysJoinDef>> fromBean2JoinDef = childBc.fromBean2JoinDef;

        Map<String,String>     childBean2TableMap = childBc.bean2Table;

        //based on child field , we can find the starting table
        if(childField.isEmpty()){
            childField = "id";
        }

        //SysField cbeanField = childFielddefs.get(childField);
        //String bfarray[]    = cbeanField.getFullField().split("\\.");
        String startTbl     = null;
        //if (bfarray.length == 1) {
            startTbl = childBean2TableMap.get(childBc.baseBean);
        //} else {
        //    startTbl = childBean2TableMap.get(bfarray[0]);
        //}

        Queue<String> queue = new LinkedList<>();
        queue.add(startTbl);
        StringBuilder JoinClause = new StringBuilder();
        JoinClause.append("join");
        JoinClause.append("  ");
        JoinClause.append(startTbl);
        JoinClause.append(" ");
        JoinClause.append(mvl + "_"+ childBc.baseBean); //alias
        JoinClause.append(" on ");
        JoinClause.append(primaryIdField.getFullField());
        JoinClause.append(" = ");
        JoinClause.append(mvl + "_"+ childBc.baseBean); //alias
        JoinClause.append(".id");

        while (!queue.isEmpty())
        {
            String currentBean   = queue.poll();
            List<SysJoinDef> joinDefList = fromBean2JoinDef.get(currentBean);
            List<String> nextBeans       = new LinkedList<>();
            if(joinDefList != null) {
                for (int i = 0; i < joinDefList.size(); i++) {
                    SysJoinDef joinDef = joinDefList.get(i);
                    String childBean   = joinDef.getChildBean();

                    if(mvlJoinTables.contains(childBean)){

                        String childTable  = childBean2TableMap.get(childBean);

                        nextBeans.add(childBean);

                        String childCol  = joinDef.getChildField();
                        String parentCol = joinDef.getParentField();

                        JoinClause.append(" join ");
                        JoinClause.append(childTable);
                        JoinClause.append(" ");
                        JoinClause.append(mvl);
                        JoinClause.append("_");
                        JoinClause.append(childBean);

                        JoinClause.append(" on ");

                        JoinClause.append(mvl);
                        JoinClause.append("_");
                        JoinClause.append(currentBean);

                        JoinClause.append(".");
                        JoinClause.append(parentCol);

                        JoinClause.append("=");

                        JoinClause.append(mvl);
                        JoinClause.append("_");
                        JoinClause.append(childBean);

                        JoinClause.append(".");
                        JoinClause.append(childCol);
                    }
                }


                if (nextBeans != null) {
                    for (int i = 0; i < nextBeans.size(); i++) {
                        queue.add(nextBeans.get(i));
                    }
                }
            }
        }
        return JoinClause.toString();
    }

    private void getMvlWhereClause(String mvl,
                                   SysBcDef bcdef,
                                   Map<String, String> f2ValKey,
                                   StringBuilder whereClause,
                                   Boolean bFirst,
                                   HashMultimap<String, String> mvl2JoinTables) {
        whereClause.append("   ");

        Map<String,SysField>   fielddefs     = bcdef.mfields;
        Map<String,String>     bean2TableMap = bcdef.bean2Table;


        for (Map.Entry<String,String> entry : f2ValKey.entrySet()) {
            String fieldName = entry.getKey();
            String fvalKey = entry.getValue();

            SysField beanField = fielddefs.get(fieldName);
            String bfarray[] = beanField.getFullField().split("\\.");

            if (bFirst) {
                bFirst = false;
            } else {
                whereClause.append(" and ");
            }

            if (bfarray.length == 1) {
                mvl2JoinTables.put(mvl,bean2TableMap.get(bcdef.baseBean));

                whereClause.append(mvl);
                whereClause.append("_");
                whereClause.append(bcdef.baseBean);
                whereClause.append(".");
                whereClause.append(beanField.getFullField());
                whereClause.append(" = #{");
                whereClause.append(fvalKey);
                whereClause.append("}");

            } else {

                mvl2JoinTables.put(mvl,bean2TableMap.get(bfarray[0]));

                whereClause.append(mvl);
                whereClause.append("_");
                whereClause.append(beanField.getFullField());
                whereClause.append(" = #{");
                whereClause.append(fvalKey);
                whereClause.append("}");
            }
        }
    }
    private void getMvlJoinClause(String mvl) {

    }

//    private void calBeanDependency(SysBcDef bcdef ,
//                                   Map<String, List<String>> beanDepson,
//                                   Map<String, List<SysJoinDef>> toBean2JoinDef,
//                                   Map<String, List<SysJoinDef>> fromBean2JoinDef) {
//        List<SysJoinDef> joindefs = bcdef.joindefs;
//
//        for (int i = 0; i < joindefs.size(); i++) {
//            SysJoinDef joinDef = joindefs.get(i);
//            String childBean = joinDef.getChildBean();
//            String parentBean = joinDef.getParentBean();
//
//
//            List<String> lstBeans = beanDepson.get(parentBean);
//
//
//            if (lstBeans != null) {
//                lstBeans.add(childBean);
//            } else {
//                lstBeans = new ArrayList<>();
//                lstBeans.add(childBean);
//            }
//            beanDepson.put(parentBean, lstBeans);
//
//            List<SysJoinDef> lstJoinDefs = toBean2JoinDef.get(childBean);
//            if (lstJoinDefs != null) {
//                lstJoinDefs.add(joinDef);
//            } else {
//                lstJoinDefs = new ArrayList<>();
//                lstJoinDefs.add(joinDef);
//            }
//            toBean2JoinDef.put(childBean, lstJoinDefs);
//
//
//            List<SysJoinDef> fromLstJoinDefs = fromBean2JoinDef.get(parentBean);
//            if(fromLstJoinDefs != null){
//                fromLstJoinDefs.add(joinDef);
//            }else{
//                fromLstJoinDefs = new ArrayList<>();
//                fromLstJoinDefs.add(joinDef);
//            }
//            fromBean2JoinDef.put(parentBean,fromLstJoinDefs);
//        }
//
//    }
    public List<Map<String, Object>> executeQuery() {
//        String baseBeanMapperClassName = bcdef.bean2Mapper.get(bcdef.baseBean);
//        try {
//            Class baseMapperBeanClass = Class.forName(baseBeanMapperClassName);
//            //baseBeanClass.getMethod("")
//            Object mapper = SpringUtil.getBean(baseMapperBeanClass) ;
//            Method queryOneBCMtd =  baseMapperBeanClass.getMethod("queryOneBC",new Class[]{SysBusComp.class});
//
//            Object result = queryOneBCMtd.invoke(mapper, this);
//            resultMap = (HashMap<String,Object>)result;
//        }catch (ClassNotFoundException cnfe){
//
//        }catch(NoSuchMethodException nsme){
//
//        }catch(IllegalAccessException iae){
//
//        }catch(InvocationTargetException ite){
//
//        }

        //select active fields from active fields tables where fieldValMap
        //1. calculate tables to pick fields or as condition
        //2. join tables starting from base table
        String querySql = buildQuerySql(this.bcdef);

        HashMap<String, Object> paramMap = new HashMap<>();

        paramMap.putAll(fieldValMap);
        paramMap.put("querySql",querySql);
        return sysBCMapper.queryBCList(paramMap);

    }

    @Transactional
    public void create() {
        Map<String, SysField> fielddefs     = this.bcdef.mfields;
        Map<String, String> bean2TableMap   = this.bcdef.bean2Table;
        Map<String, List<SysJoinDef>> toBean2JoinDef = this.bcdef.toBean2JoinDef;
        Map<String, Map<String, Object>> Bean2ColValListMap = new HashMap<String, Map<String, Object>>();
        Map<String, List<String>> beanDepson = this.bcdef.beanDepson;
        for (Map.Entry<String, Object> entry : this.fieldValMap.entrySet()) {
            String fieldName = entry.getKey();
            Object fieldVal  = entry.getValue();

            SysField beanField = fielddefs.get(fieldName);
            if( bcdef.mfields.get(fieldName).isReadOnly())
            {
                //ignore this field.
                continue;
            }
            String bfarray[] = beanField.getFullField().split("\\.");

            if (bfarray.length == 1) {
                Map<String, Object> col2ValMap = Bean2ColValListMap.get(this.bcdef.baseBean);
                if (col2ValMap != null) {
                    col2ValMap.put(bfarray[0], fieldVal);

                    Bean2ColValListMap.put(this.bcdef.baseBean, col2ValMap);
                } else {
                    col2ValMap = new HashMap<>();
                    col2ValMap.put(bfarray[0], fieldVal);

                    Bean2ColValListMap.put(this.bcdef.baseBean, col2ValMap);
                }
            } else {
                Map<String, Object> col2ValMap = Bean2ColValListMap.get(bfarray[0]);

                if (col2ValMap != null) {
                    col2ValMap.put(bfarray[1], fieldVal);
                    Bean2ColValListMap.put(bfarray[0], col2ValMap);
                } else {
                    col2ValMap = new HashMap<>();
                    col2ValMap.put(bfarray[1], fieldVal);
                    Bean2ColValListMap.put(bfarray[0], col2ValMap);
                }

            }
        }


        Queue<String> queue = new LinkedList<String>();
        queue.add(bcdef.baseBean);
        while (!queue.isEmpty()) {
            String currentBean = queue.poll();

            String currentTable = bean2TableMap.get(currentBean);
            Map<String, Object> col2ValMap = Bean2ColValListMap.get(currentBean);

            if (col2ValMap != null) {
                List<SysJoinDef> joinDefs = toBean2JoinDef.get(currentBean);
                if (joinDefs != null) {
                    for (int i = 0; i < joinDefs.size(); i++) {
                        SysJoinDef joinDef = joinDefs.get(i);

                        String parentBean = joinDef.getParentBean();
                        String parentCol = joinDef.getParentField();
                        String childCol = joinDef.getChildField();

                        Map<String, Object> record = Bean2ColValListMap.get(parentBean);

                        Object val = record.get(parentCol);
                        col2ValMap.put(childCol, val);
                    }
                }
                col2ValMap.put("table", currentTable);

                Date now = new Date();

                col2ValMap.put("create_time", now);
                col2ValMap.put("create_by", "admin");
                col2ValMap.put("update_time", now);
                col2ValMap.put("update_by", "admin");

                sysBCMapper.Insert2(col2ValMap);
                List<String> nextBeans = beanDepson.get(currentBean);
                if (nextBeans != null) {
                    for (int i = 0; i < nextBeans.size(); i++) {
                        queue.add(nextBeans.get(i));
                    }
                }
            }

        }

    }

    @Transactional
    public void updateById() {
        Map<String, List<String>> beanDepson = this.bcdef.beanDepson;
        if (fieldValMap.get("id") != null) {
            Map<String, SysField> fielddefs     = this.bcdef.mfields;
            Map<String, String> bean2TableMap = this.bcdef.bean2Table;
            Map<String, List<SysJoinDef>> toBean2JoinDef = this.bcdef.toBean2JoinDef;
            Map<String, Map<String, Object>> Bean2ColValListMap = new HashMap<String, Map<String, Object>>();

            for (Map.Entry<String, Object> entry : this.fieldValMap.entrySet()) {
                String fieldName = entry.getKey();
                Object fieldVal = entry.getValue();

                SysField beanField = fielddefs.get(fieldName);
                if( beanField.isReadOnly())
                {
                    //ignore this field.
                    continue;
                }
                String bfarray[] = beanField.getFullField().split("\\.");

                if (bfarray.length == 1) {
                    Map<String, Object> col2ValMap = Bean2ColValListMap.get(this.bcdef.baseBean);
                    if (col2ValMap != null) {
                        col2ValMap.put(bfarray[0], fieldVal);

                        Bean2ColValListMap.put(this.bcdef.baseBean, col2ValMap);
                    } else {
                        col2ValMap = new HashMap<>();
                        col2ValMap.put(bfarray[0], fieldVal);

                        Bean2ColValListMap.put(this.bcdef.baseBean, col2ValMap);
                    }
                } else {
                    Map<String, Object> col2ValMap = Bean2ColValListMap.get(bfarray[0]);

                    if (col2ValMap != null) {
                        col2ValMap.put(bfarray[1], fieldVal);
                        Bean2ColValListMap.put(bfarray[0], col2ValMap);
                    } else {
                        col2ValMap = new HashMap<>();
                        col2ValMap.put(bfarray[1], fieldVal);
                        Bean2ColValListMap.put(bfarray[0], col2ValMap);
                    }

                }
            }

            Queue<String> queue = new LinkedList<String>();
            queue.add(bcdef.baseBean);
            while (!queue.isEmpty()) {
                String currentBean = queue.poll();

                String currentTable                 = bean2TableMap.get(currentBean);
                Map<String, Object> col2ValMap      = Bean2ColValListMap.get(currentBean);
                Map<String, Object> whereCol2ValMap = new HashMap<>();

                //this table needs to be updated ( get columns to be updated and where conditions )
                if (col2ValMap != null) {
                    List<SysJoinDef> joinDefs = toBean2JoinDef.get(currentBean);
                    if (joinDefs != null) {
                        for (int i = 0; i < joinDefs.size(); i++) {
                            SysJoinDef joinDef = joinDefs.get(i);

                            String parentBean = joinDef.getParentBean();
                            String parentCol  = joinDef.getParentField();
                            String childCol   = joinDef.getChildField();

                            Map<String, Object> record = Bean2ColValListMap.get(parentBean);

                            Object val = record.get(parentCol);

                            col2ValMap.put(childCol, val);
                            whereCol2ValMap.put(childCol, val);
                        }
                    } else {
                        whereCol2ValMap.put("id", fieldValMap.get("id"));
                    }

                    col2ValMap.put("table", currentTable);
                    col2ValMap.put("condition", whereCol2ValMap);

                    Date now = new Date();

                    col2ValMap.put("create_time", now);
                    col2ValMap.put("create_by", "admin");
                    col2ValMap.put("update_time", now);
                    col2ValMap.put("update_by", "admin");


                    sysBCMapper.Update(col2ValMap);

                    List<String> nextBeans = beanDepson.get(currentBean);
                    if (nextBeans != null) {
                        for (int i = 0; i < nextBeans.size(); i++) {
                            queue.add(nextBeans.get(i));
                        }
                    }
                }

            }
        }

    }

    @Transactional
    public void deleteById() {
        //1. calculate all tables should be deleted and active fields from join definitions with extension =true
        //2.

        Map<String, String> bean2TableMap = this.bcdef.bean2Table;
        Map<String, List<SysJoinDef>> toBean2JoinDef = this.bcdef.toBean2JoinDef;
        Map<String, List<String>> beanDepson = this.bcdef.beanDepson;
        Queue<String> queue = new LinkedList<>();
        queue.add(bcdef.baseBean);

        Stack<String> deleteSqls = new Stack<>();

        while (!queue.isEmpty()) {
            String currentBean = queue.poll();

            String currentTable = bean2TableMap.get(currentBean);

            StringBuffer deleteClause = new StringBuffer();
            StringBuffer whereClause  = new StringBuffer();

            deleteClause.append(" delete ");
            deleteClause.append(currentBean);
            deleteClause.append(" from ");
            deleteClause.append(currentTable);
            deleteClause.append(" ");
            deleteClause.append(currentBean);

            StringBuffer joinClause   = new StringBuffer();
            List<SysJoinDef> joinDefs = toBean2JoinDef.get(currentBean);

            Queue<List<SysJoinDef>> joindefsQ = new LinkedList<>();
            joindefsQ.add(joinDefs);
            while (!joindefsQ.isEmpty()) {
                List<SysJoinDef> currentJoinDefs = joindefsQ.poll();
                if(currentJoinDefs != null) {
                    for (int i = 0; i < currentJoinDefs.size(); i++) {
                        SysJoinDef joindef = joinDefs.get(i);
                        String parentBean  = joindef.getParentBean();
                        String parentTable = bean2TableMap.get(parentBean);
                        String parentCol   = joindef.getParentField();
                        String childBean   = joindef.getChildBean();
                        String childCol    = joindef.getChildField();

                        joinClause.append(" join ");
                        joinClause.append(parentTable);
                        joinClause.append(" ");
                        joinClause.append(parentBean);
                        joinClause.append(" on ");
                        joinClause.append(parentBean);
                        joinClause.append(".");
                        joinClause.append(parentCol);
                        joinClause.append(" = ");
                        joinClause.append(childBean);
                        joinClause.append(".");
                        joinClause.append(childCol);

                        List<SysJoinDef> nextJoinDefs = toBean2JoinDef.get(parentBean);

                        if (nextJoinDefs != null) {
                            joindefsQ.add(nextJoinDefs);
                        }
                        else{
                            whereClause.append(" where " );
                            whereClause.append(bcdef.baseBean);
                            whereClause.append(".id = ");
                            whereClause.append( " #{id} " );
                        }
                    }
                }else{
                    whereClause.append(" where " );
                    whereClause.append(bcdef.baseBean);
                    whereClause.append(".id = ");
                    whereClause.append( " #{id} " );
                }

            }

            if( !joinClause.toString().isEmpty())
                deleteClause.append(joinClause);

            if( !whereClause.toString().isEmpty())
                deleteClause.append(whereClause);

            String deleteSql = deleteClause.toString();
            deleteSqls.push(deleteSql);

            List<String> nextBeans = beanDepson.get(currentBean);
            if (nextBeans != null) {
                queue.addAll(nextBeans);
            }
        }
        HashMap<String,Object> paramMap = new HashMap<>();
        while (!deleteSqls.isEmpty()){
            String deleteSql = deleteSqls.pop();

            paramMap.clear();
            paramMap.putAll(fieldValMap);
            paramMap.put("deleteSql",deleteSql);

            sysBCMapper.delete(paramMap);
        }
    }

}
